/**
 **************************************************************************************************************************
 * Wrapper for D3D11
 **************************************************************************************************************************
 * @file RBD3D11Renderer.h
 **************************************************************************************************************************/

#pragma once

#ifdef  HEADER_MSG
#pragma HEADER_MSG(RBD3D11Renderer.h)
#endif


/**************************************************************************************************************************
 * Includes
 **************************************************************************************************************************/
#include "RBMain/RBRenderer.h"

#include "RBMain/RBPixelFormat.h"

/**************************************************************************************************************************
 * Forward declarations
 **************************************************************************************************************************/
struct ID3D11Device;
struct IDXGISwapChain;
struct IDXGIFactory;
struct ID3D11DeviceContext;
struct ID3D11Texture2D;

/**************************************************************************************************************************
 * Classes
 **************************************************************************************************************************/

struct RBD3D11RendererInit
{
    uint32 Width;
    uint32 Height;
    RBPixelFormat DisplayFormat;
    void* OutputWindow;
    bool Windowed;

    RBD3D11RendererInit
    (
		void* outputWindow = NULL,
        uint32 width = 0,
        uint32 height = 0,
        bool windowed = true,
        int32 adapter = 0,
        RBPixelFormat displayFormat = PF_R8G8B8A8_UNORM_SRGB,
        uint32 msaaCount = 1,
        uint32 msaaQuality = 0
    )
    : Width(width)
    , Height(height)
    , DisplayFormat(displayFormat)
    , OutputWindow(outputWindow)
    , Windowed(windowed)
    {
    }

};

class RBD3D11Renderer : public RBRenderer
{
public:

    static RBD3D11Renderer*     GetSingleton()      { return static_cast<RBD3D11Renderer*>(RBRenderer::GetSingleton()); }

    static bool					create_device		(ID3D11Device** device);
    static bool					create_device_swap_chain(ID3D11Device** device, IDXGISwapChain** swap_chain, const RBD3D11RendererInit& init);

	// Initialization of the device
    bool                        init                (ID3D11Device* device, IDXGISwapChain* swap_chain);
    bool                        init_no_swap_chain  (ID3D11Device* device, const RBD3D11RendererInit& init);

    void                        change_size         (int width, int height);
    void                        Release             ();


    ID3D11Device*               GetDevice() const;
    ID3D11DeviceContext*        GetDeviceContext() const;

                                RBD3D11Renderer();
                               ~RBD3D11Renderer();

    virtual void                OMSetRenderTargets  (uint32 view_count, const RBRenderTargetView* const* colors, RBDepthStencilView* depth);
	virtual void                ClearRenderTargets  (RBRenderTargetGroup* pRenderTargetGroup, const RBColorA& color, float32 clearZ, uint32 clearFlags);
    virtual void                Swap                ();
	virtual void                Flush               ();
    virtual void                DrawIndexedPrimitive(uint32 uiVertexCount, uint32 uiPrimitiveCount, int32 iBaseVertexIndex, uint32 uiStartIndex);
    virtual void                DrawPrimitive       (uint32 uiPrimitiveCount, uint32 uiStartVertex);

    virtual void                IASetVertexBuffer   (uint32 uiStream, const RBVertexBuffer* pVertexBuffer, bool bUseInstancing = false);
    virtual void                IASetVertexDecl     (const RBVertexDeclaration* pVertexDecl);
    virtual void                IASetIndexBuffer    (const RBIndexBuffer* pIndexBuffer);
    virtual void                IASetTopology       (RBGeometryPrimitiveType::EType ePT);

    virtual void                Map                 (void* pResource, uint32 mapFlags, rb_locked_data* pMapped, uint32 sub_resource = 0);
    virtual void                Unmap               (void* pResource, uint32 sub_resource = 0);

    virtual void                VSSetConstantBuffers(uint32 uiStart, uint32 uiCount, void** vBuffers);
    virtual void                PSSetConstantBuffers(uint32 uiStart, uint32 uiCount, void** vBuffers);

    virtual void                VSSetShaderResources(uint32 uiStart, uint32 uiCount, void** vNativeViews);
    virtual void                PSSetShaderResources(uint32 uiStart, uint32 uiCount, void** vNativeViews);

    virtual void                VSSetSamplers       (uint32 uiStart, uint32 uiCount, void** vNativeSamplers);
    virtual void                PSSetSamplers       (uint32 uiStart, uint32 uiCount, void** vNativeSamplers);


    virtual void                VSSetShader         (const RBShader* pShader);
    virtual void                PSSetShader         (const RBShader* pShader);

    virtual void                OMSetBlendState     (const RBBlendState* pBlendState);
    virtual void                RSSetState          (const RBRasterizerState* pState);
    virtual void                OMSetDepthStencilState(const RBDepthStencilState* pState);

    virtual void                CopyResource        (RBHardwareBuffer* dst, RBHardwareBuffer* src);
    virtual void                RSSetViewport       (const RBViewPort& viewport);

    virtual void                ClearState          ();

private:

    bool                        init_common         (ID3D11Device* device);

    void                        create_back_buffer  ();
    void                        release_back_buffer ();

    IDXGISwapChain*     m_swap_chain; // optional swap chain
    ID3D11Device*       m_device;
    ID3D11DeviceContext* m_immediate_context;

    RBD3D11RendererInit         m_init_params;
};